<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <script>
      // 将普通任务转换成异步 微任务
      function createAsyncTask(task) {
        if (typeof queueMicrotask === 'function') {
          queueMicrotask(() => {
            task()
          })
        } else {
          setTimeout(() => {
            task()
          }, 0)
        }
      }

      //抽取复用代码
      function handleePromise(p2, x, resolve, reject) {
        // 判断是不是循环引用（重复引用）
        if (x === p2) {
          throw new TypeError('Chaining cycle detected for promise #<Promise>')
        }
        // 判断x是不是promise实例对象
        if (x instanceof MyPromise) {
          x.then(
            (res) => {
              resolve(res)
            },
            (err) => {
              reject(err)
            }
          )
        } else {
          resolve(x)
        }
      }

      const PENDING = 'pending'
      const REJECTED = 'rejected'
      const FULFILLED = 'fulfilled'

      // 类
      class MyPromise {
        #handles = [] // 私有属性 用来保存回调函数
        // 构造函数
        constructor(func) {
          this.state = PENDING // 初始状态
          this.result = undefined // 初始结果

          const resolve = (result) => {
            if (this.state === PENDING) {
              this.state = FULFILLED
              this.result = result
              this.#handles.forEach((v) => {
                v.onFulfilled()
              })
            }
          }
          const reject = (result) => {
            if (this.state === PENDING) {
              this.state = REJECTED
              this.result = result
              this.#handles.forEach((v) => {
                v.onRejected()
              })
            }
          }
          try {
            func(resolve, reject)
          } catch (error) {
            reject(error)
          }
        }
        // 实例方法
        then(onFulfilled, onRejected) {
          onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : (x) => x
          onRejected =
            typeof onRejected === 'function'
              ? onRejected
              : (x) => {
                  throw x
                }
          // then方法需要返回一个新的 Promise实例
          const p2 = new MyPromise((resolve, reject) => {
            if (this.state === FULFILLED) {
              createAsyncTask(() => {
                try {
                  // x是then的第一个形参函数的返回值
                  let x = onFulfilled(this.result)
                  handleePromise(p2, x, resolve, reject)
                } catch (error) {
                  reject(error)
                }
              })
            } else if (this.state === REJECTED) {
              createAsyncTask(() => {
                try {
                  // x是then的第一个形参函数的返回值
                  let x = onRejected(this.result)
                  handleePromise(p2, x, resolve, reject)
                } catch (error) {
                  reject(error)
                }
              })
            } else if (this.state === PENDING) {
              // 解决异步 ,保存成功/失败回调函数
              this.#handles.push({
                onFulfilled: () => {
                  createAsyncTask(() => {
                    try {
                      // x是then的第一个形参函数的返回值
                      let x = onFulfilled(this.result)
                      handleePromise(p2, x, resolve, reject)
                    } catch (error) {
                      reject(error)
                    }
                  })
                },
                onRejected: () => {
                  createAsyncTask(() => {
                    try {
                      // x是then的第一个形参函数的返回值
                      let x = onRejected(this.result)
                      handleePromise(p2, x, resolve, reject)
                    } catch (error) {
                      reject(error)
                    }
                  })
                }
              })
            }
          })
          return p2
        }
        catch(onRejected) {
          // 内部调用then函数
          return this.then(undefined, onRejected)
        }
        finally(onFinally) {
          this.then(onFinally, onFinally)
        }
        // 静态方法
        static resolve(value) {
          if (value instanceof MyPromise) {
            return value
          }
          return new Mypromise((resolve, reject) => {
            resolve(value)
          })
        }

        static reject(value) {
          if (value instanceof MyPromise) {
            return value
          }
          return new Mypromise((resolve, reject) => {
            reject(value)
          })
        }

        static race(promises) {
          return new MyPromise((resolve, reject) => {
            if (!Array.isArray(promises)) {
              return reject(new TypeError('argument is not iterable'))
            }
            promises.forEach((p) => {
              MyPromise.resolve(p).then(
                (res) => {
                  resolve(res)
                },
                (err) => {
                  reject(err)
                }
              )
            })
          })
        }

        static all(promises) {
          return new MyPromise((resolve, reject) => {
            // 判断参数是不是数组
            if (!Array.isArray(promises)) {
              return reject(new TypeError('argument is not iterable'))
            }
            // 空数组 直接兑现
            promises.length == 0 && resolve(promises)
            let resolves = []
            let count = 0
            promises.forEach((p, index) => {
              MyPromise.resolve(p).then(
                (res) => {
                  // 保证结果跟 Promise 数组参数顺序一致
                  resolves[index] = res
                  count++
                  count === promises.length && resolve(resolves) // 全部兑现之后，再兑现结果
                },
                (err) => {
                  reject(err)
                }
              )
            })
          })
        }
      }

      // ---------------------------测试代码----------------------------------
      const p1 = new MyPromise((resolve, reject) => {
        resolve('1成功')
        // reject('1错误')
        // throw new Error('error')
        // setTimeout(() => {
        //   resolve('1成功')
        // }, 2000)
      })

      const p2 = p1
        .then((res) => {
          console.log('成功then1:', res)
          // throw new Error('error')
          return new MyPromise((resolve, reject) => {
            setTimeout(() => {
              reject(222)
            }, 2000)
          })
        })
        .catch((err) => {
          console.log(err)
        })
        .finally(() => {
          console.log('finally')
        })
    </script>
  </body>
</html>
